//
//  HOMIteratedArgument.m
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 29/08/06.
//  Copyright 2006 Ofri Wolfus. All rights reserved.
//

#include <objc/objc-class.h>
#include <objc/objc-runtime.h>
#import "HOMIteratedArgument.h"
#import "HOMUtilities.h"


extern Method _hom_findMethodForSelector(SEL sel);


@interface HOMIteratedArrayArgument : HOMIteratedArgument {
	NSArray		*contents;
	IMP			objectAtIndex;
}

- (id)initWithArray:(NSArray *)array;

@end

@interface HOMIteratedObjectsArray : HOMIteratedArgument {
	id			*objects;
}

- (id)initWithObjects:(id *)objects count:(unsigned)count;

@end


@implementation HOMIteratedArgument

+ (id)iteratedArgumentWithArray:(NSArray *)array {
	return [[[HOMIteratedArrayArgument alloc] initWithArray:array] autorelease];
}

+ (id)iteratedArgumentWithObjects:(id *)objects count:(unsigned)count {
	return [[[HOMIteratedObjectsArray alloc] initWithObjects:objects count:count] autorelease];
}

- (id)init {
	if ((self = [super init])) {
		nextIndex = 0U;
	}
	
	return self;
}

- (id)nextObject {
	DP_SUBCLASS_MUST_IMPLEMENT;
	return nil;
}

- (BOOL)isFinished {
	return nextIndex >= count;
}

- (unsigned)count {
	return count;
}

- (void)reset {
	nextIndex = 0U;
}

@end


@implementation HOMIteratedArrayArgument

- (id)initWithArray:(NSArray *)array {
	if ((self = [super init])) {
		contents = [array retain];
		count = [contents count];
		objectAtIndex = [contents methodForSelector:@selector(objectAtIndex:)];
	}
	
	return self;
}

- (void)dealloc {
	[contents release]; contents = nil;
	[super dealloc];
}

- (id)nextObject {
	if (count > nextIndex) {
		++nextIndex;
		return objectAtIndex(contents, @selector(objectAtIndex:), nextIndex - 1U);
	} else {
		return objectAtIndex(contents, @selector(objectAtIndex:), count - 1U);
	}
}

- (id)forward:(SEL)sel :(marg_list)args {
	Method		m = (count > 0U ? _hom_findMethodForSelector(sel) : NULL);
	unsigned	i;
	unsigned	size = m ? method_getSizeOfArguments(m) : 0U;
	
	DPAssert(m != NULL, @"Can't find a method for the selector: %s", NSStringFromSelector(sel));
	
	for (i = 0U; i < count; i++)
		objc_msgSendv(objectAtIndex(contents, @selector(objectAtIndex:), i), sel, size, args);
	
	return self;
}

@end


@implementation HOMIteratedObjectsArray

- (id)initWithObjects:(id *)objs count:(unsigned)c {
	if ((self = [super init])) {
		NSAssert(objs != NULL, @"Passed objects array must not be NULL");
		objects = objs;
		count = c;
	}
	
	return self;
}

- (void)dealloc {
	free(objects); objects = NULL;
	[super dealloc];
}

- (id)nextObject {
	if (count > nextIndex) {
		++nextIndex;
		return objects[nextIndex - 1U];
	} else {
		return objects[count - 1U];
	}
}

- (id)forward:(SEL)sel :(marg_list)args {
	Method		m = (count > 0U ? _hom_findMethodForSelector(sel) : NULL);
	unsigned	i;
	unsigned	size = m ? method_getSizeOfArguments(m) : 0U;
	
	DPAssert(m != NULL, @"Can't find a method for the selector: %s", NSStringFromSelector(sel));
	
	for (i = 0U; i < count; i++)
		objc_msgSendv(objects[i], sel, size, args);
	
	return self;
}

@end
